import { effect } from "../reactivity/index";
import { ShapeFlags } from "../shared/shapeFlags";
import { createAppAPI } from "./apiCreateApp";
import { invokeArrayFns } from "./apiLifecycle";
import { createComponentInstance, setupComponent } from "./component";
import { queueJob } from "./scheduler";

export function createRender(options) { // options是平台传过来的方法，不同的平台可以实现不同的操作逻辑 平台比如有 浏览器，小程序等
  return baseCreateRenderer(options);
  
}

function baseCreateRenderer(options) {

  const {
    createElement: hostCreateElement,
    patchProp: hostPatchProp,
    setElementText: hostSetElementText,
    insert: hostInsert,
    remove: hostRemove
  } = options

  const mountElement = (vnode,container,anchor) => {

    let {shapeFlag,props} = vnode

    let el = vnode.el = hostCreateElement(vnode.type);
    
    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      hostSetElementText(el,vnode.children)
    } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      mountChildren(vnode.children,el)
    }

    if(props) {
      for(let key in props) {
        hostPatchProp(el,key,null,props[key])
      }
    }

    hostInsert(el,container,anchor)
  }

  const mountChildren = (children,container) => {
    for(let i = 0; i < children.length; i++) {
      patch(null,children[i],container)
    }
  }


  const patchProps = (oldProps,newProps,el) => {
    if (oldProps !== newProps) {
      // 新的属性需要覆盖
      for(let key in newProps) {
        const prev = oldProps[key]
        const next = newProps[key]
        if(prev != next) {
          hostPatchProp(el,key,prev,next)
        }
      }
      // 老的有，新的没有，需要把老的删除掉

      for(const key in oldProps) {
        if(!(key in newProps)) {
          hostPatchProp(el,key,oldProps[key],null)
        }
      }
    }
  }

  const patchKeydChildren = (c1,c2,el) => {
    // 内部有优化策略
    let i = 0;
    let e1 = c1.length - 1;
    let e2 = c2.length - 1;
    // 从前面比对
    while (i <= e1 && i <= e2) {
      const n1 = c1[i];
      const n2 = c2[i];
      if (isSameVnodeType(n1,n2)) {
        patch(n1,n2,el)
      } else {
        break
      }
      i++;
    }

    // 从后面比对
    while(i <= e1 && i <= e2) {
      const n1 = c1[e1];
      const n2 = c2[e2];
      if (isSameVnodeType(n1,n2)) {
        patch(n1,n2,el)
      } else {
        break
      }
      e1--;
      e2--;
    }

    // 只考虑元素新增和删除的情况
    // 只要i 大于 e1 表示新增属性
    if (i > e1) {// 说明有新增的情况
      if (i <= e2) { // 表示有新增的部分
        // 先根据e2取他的下一个元素 和 数组长度进行对比
        const nextPos = e2 + 1;
        const anchor = nextPos < c2.length ? c2[nextPos].el : null;
        while ( i <= e2) {
          patch(null, c2[i], el, anchor);
          i++;
        }
      } 
    } else if (i > e2) { // 说明是删除的情况
      while (i <= e1) {
        hostRemove(c1[i].el);
        i++;
      }
    } else {
      // 无规律情况的diff
      
      const s1 = i;
      const s2 = i;
      const keyToNewIndexMap = new Map();
      for(let i = s2; i <= e2; i++) {
        const nextChild = c2[i];
        keyToNewIndexMap.set(nextChild.key,i);
      }
      const toBePatched = e2 - s2 + 1;
      const newIndexToOldMapIndex = new Array(toBePatched).fill(0);
      // debugger
      // 只是做相同属性的diff 但是位置可能还不对，新的插入的还没有插入
      for (let i = s1; i <= e1; i++) {
        const prevChild = c1[i];
        let newIndex = keyToNewIndexMap.get(prevChild.key); // 获取新的索引
        if (newIndex == undefined) {
          hostRemove(prevChild.el) // 老的有， 新的没有直接删除
        } else {
          newIndexToOldMapIndex[newIndex - s2] = i + 1;
          patch(prevChild,c2[newIndex],el)
        }
      }
      
      // for(let i = toBePatched - 1; i >= 0; i--) {
      //   const nextIndex = s2 + i;
      //   const nextChild = c2[nextIndex];
      //   let anchor = nextIndex + 1 < c2.length ? c2[nextIndex + 1].el : null;
      //   if(newIndexToOldMapIndex[i] == 0) {
      //     patch(null,nextChild, el ,anchor)
      //   } else {
      //     // 根据参照物，将节点直接移动过去(所有节点都要，但是有些节点可以不用移动)
      //     hostInsert(nextChild.el,el,anchor)
      //   }
      // }
      // 使用最长递增子序列来优化，可以尽可能的少移动节点
      let increasingNewIndexSequence = getSequence(newIndexToOldMapIndex)
      let j = increasingNewIndexSequence.length - 1

      // 处理虚拟节点的位置 从后往前依次插入节点
      for (let i = toBePatched - 1; i >= 0; i--) {
        // 找到当前最后一个 和 当前节点
        let currentIndex = i + s2
        let child = c2[currentIndex]

        // 插入的参照物
        let anchor = currentIndex + 1 < c2.length ? c2[currentIndex+1].el  : null
        debugger
        // 如果是0 说明没有被patch过 直接生成
        if(newIndexToOldMapIndex[i] == 0) {
          patch(null, child, el, anchor)
        } else {

          // 从后往前查 没有就插入 
          if(i != increasingNewIndexSequence[j]){
            hostInsert(child.el, el, anchor)
          } else {
            j--;
          }

        }
      }
    }
  }

  const patchChildren = (n1,n2,el) => {

    const c1 = n1.children;
    const c2 = n2.children;

    // 老的是文本，新的是文本 =》 新的覆盖掉老的
    // 老的是数组，新的是文本 =》 覆盖掉老的即可
    // 老的是文本，新的是数组 =》 移除老的文本，生成新的节点塞进去
    // 老的是数组，新的也是数字 =》 核心的diff算法
    const prevShapeFlag = n1.shapeFlag;
    const shapeFlag = n2.shapeFlag;

    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      // 老的是文本 新的是文本 =》 新的覆盖掉老的
      // 老的是数组，新的是文本 =》 覆盖掉老的即可
      if (c2 != c1) {
        hostSetElementText(el, c2);
      }
    } else {
      // 新的是数组
      if(prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
        // 老的是数组，新的是数组 => diff核心算法
        // console.log('diff算法')，有key的情况
        patchKeydChildren(c1,c2,el)
      } else {
        // 新的是数组，老的可能是文本
        if(prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
          // 移除老的文本
          hostSetElementText(el, '')
        }
        if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
          // 去把新的元素进行挂载 生成新的节点塞进去
          for (let i =0; i < c2.length; i++) {
            patch(null, c2[i], el)
          }
        }
      }
    }

  }

  const patchElement = (n1,n2,container) => {
    // 如果n1和n2的类型一样
    // console.log(n1,n2)
    let el = n2.el = n1.el
    const oldProps = n1.props || {};
    const newProps = n2.props || {};

    patchProps(oldProps,newProps,el); // 比对前后属性的元素差异

    patchChildren(n1,n2,el) // 儿子的比较是核心
    
  }
  const mountComponent = (initialVnode,container) => {

    //组件的挂载逻辑
    // 1 创建组件的实例
    // 2 找到组件的render方法
    // 3 执行render
    const instance = initialVnode.component = createComponentInstance(initialVnode);

    setupComponent(instance)

    setupRenderEffect(instance,initialVnode,container)
     
  }

  const setupRenderEffect = (instance,initialVnode,container) => {
    effect(function componentEffect(){
      if (!instance.isMounted) {

        console.log(instance)

        let {bm, m} = instance

        if(bm) {
          invokeArrayFns(bm)
        }

        const subTree = instance.subTree = instance.render(); // 组件渲染对应的结果
        
        patch(null,subTree,container);
        instance.isMounted = true;

        if(m) {
          invokeArrayFns(m)
        }

      } else {

        let { bu, u } = instance;

        if (bu) {
          invokeArrayFns(bu)
        }

        //更新逻辑
        let prev = instance.subTree;
        let next = instance.render();
        patch(prev,next,container)

        if(u) {
          invokeArrayFns(u)
        }

      }
    }, {
      scheduler: queueJob
    })
  }


  const updateComponent = (n1,n2,container) => {
    
  }

  const processElement = (n1,n2,container,anchor) => {
    if (n1 == null) {
      mountElement(n2,container,anchor)
    } else {
      patchElement(n1,n2,container)
    }
  }

  const processComponent = (n1,n2,container) => {
    if (n1 == null) {
      mountComponent(n2,container)
    } else {
      updateComponent(n1,n2,container)
    }
  }

  const render = (vnode,container) => {
    patch(null,vnode,container)
  }

  const isSameVnodeType = (n1,n2) => {
    return n1.type == n2.type && n1.key === n2.key
  }

  const patch = (n1,n2,container,anchor=null) => {

    // 需要将虚拟节点变成真实节点

    if(n1 && !isSameVnodeType(n1,n2)) {
      // 删除老节点
      hostRemove(n1.el);
      n1 = null;

    }

    let { shapeFlag } = n2;
    if (shapeFlag & ShapeFlags.ELEMENT) {
      processElement(n1,n2,container,anchor)
    } else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
      processComponent(n1,n2,container)
    }
  }

  return {
    createApp: createAppAPI(render)
  }
}

// 最长递增子序列
function getSequence(arr) {
  const len = arr.length
  const result = [0]
  const p = arr.slice(0)
  let start;
  let end;
  let middle;
  for (let i = 0; i < len; i++) {
      const arrI = arr[i]
      if (arrI !== 0) {
          let resultLastIndex = result[result.length - 1]
          // 取到索引对应的值
          if (arr[resultLastIndex] < arrI) {
              p[i] = resultLastIndex
              result.push(i)
              continue
          }
          start = 0;
          end = result.length - 1;
          while (start < end) {
              middle = ((start + end) / 2) | 0
              if (arr[result[middle]] < arrI) {
                  start = middle + 1
              } else {
                  end = middle
              }
          }

          if (arrI < arr[result[start]]) {
              if (start > 0) {
                p[i] = result[start - 1]
              }
              result[start] = i
          }
      }
  }
  let len1 = result.length
  let last = result[len1 - 1]
  while (len1-- > 0) {
      result[len1] = last
      last = p[last]
  }
  return result
}